

#pragma once

#include "SocketBase.hpp"
#include <string>
#include <functional>
#include <map>
#include <list>
#include <mutex>

CYBERTRON_BEGIN

class SocketTcpClient : public SocketBase
{
public:
	CybertronCoreAPI SocketTcpClient();
	CybertronCoreAPI virtual ~SocketTcpClient();

	
	CybertronCoreAPI bool resolverName(const std::string& ip, int port);
	CybertronCoreAPI bool connect(const std::string& ip, int port);
	CybertronCoreAPI bool connectBlocked(const std::string& ip, int port);

	CybertronCoreAPI virtual void close(const char* reason);

	CybertronCoreAPI void onTimer(bool bSingleMsg = false); // bSingleMsg to pop only one message from buffer

	CybertronCoreAPI void setHeartbeatSeconds(unsigned int seconds);
	CybertronCoreAPI unsigned int getHeartbeatSeconds() const;

	CybertronCoreAPI void setHandlerDisconnected(std::function<void()> funcOnDisconnected);
	CybertronCoreAPI void setHandlerConnected(std::function<void()> funcOnConnected);
	CybertronCoreAPI void setHandlerMessage(std::function<void(Message&)> funcOnMessage);
	CybertronCoreAPI void setHandlerMessage(
		std::uint16_t msgId,
		std::function<void(Message&)> funcOnMessage);

protected:
	CybertronCoreAPI virtual void onDisconnected();
	CybertronCoreAPI virtual void onConnected();
	CybertronCoreAPI virtual void onMessage(Message& msg);

private:
	friend class TcpClientBuiltinEventHandler;
	void notifyDisconnected();
	void notifyConnected();
	void notifyMessage(Message& msg);

	enum ESocketEvent
	{
		ESocketEvent_Connected,
		ESocketEvent_Disconnected,
		ESocketEvent_Message,
	};

	void processOneMessage(ESocketEvent eventType, Message& msg);

private:
	void closeThis();

private:
	std::shared_ptr<void> mpContext;

	std::function<void()> mFuncOnDisconnected;
	std::function<void()> mFuncOnConnected;
	std::function<void(Message&)> mFuncOnMessage;
	std::map<std::uint16_t, std::function<void(Message&)>> mFuncOnMessages;

	mutable std::recursive_mutex mEventMutex;
	std::list<Message> mEventMessages;
	std::list<ESocketEvent> mEventTypes;
	bool mInOnTimer;
};

CYBERTRON_END
